#include "Avalon/WebServicesTests/WebServiceNodeTester.hpp"
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/IO/ClientConnection.hpp"
#include "Avalon/IO/Writer.hpp"
#include "Avalon/IOTests/MockServerConnection.hpp"
#include "Avalon/Serialization/JsonDataShuttleFactory.hpp"
#include "Avalon/Services/Service.hpp"
#include "Avalon/Services/TupleMessage.hpp"
#include "Avalon/SignalHandling/SignalSink.hpp"
#include "Avalon/WebServices/HttpSession.hpp"
#include "Avalon/WebServices/HttpSessionFactory.hpp"
#include "Avalon/WebServices/HttpServerPredicates.hpp"
#include "Avalon/WebServices/HttpServerRequest.hpp"
#include "Avalon/WebServices/HttpServerResponse.hpp"

AVALON_DECLARE_SERVICE(TestServiceA, "TestServiceA", int, int);

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Serialization;
using namespace Avalon::SignalHandling;
using namespace Avalon::WebServices;
using namespace Avalon::WebServices::Tests;
using namespace boost;
using namespace boost::posix_time;
using namespace std;

void WebServiceNodeTester::setUp() {
  m_serverConnection = new MockServerConnection();
  m_server.reset(new HttpServer(m_serverConnection));
  CPPUNIT_ASSERT_NO_THROW(m_server->Start());
  m_webServiceNode.reset(new WebServiceNode(m_server.get(),
    new HttpSessionFactory(), new JsonDataShuttleFactory()));
  m_sessionSink = SignalSink::Create();
  m_channel.reset(new MockClientChannel(m_serverConnection));
  CPPUNIT_ASSERT_NO_THROW(static_cast<ClientConnection&>(
    m_channel->GetConnection()).Connect());
}

void WebServiceNodeTester::tearDown() {
  m_webServiceNode.reset();
  m_server.reset();
}

void WebServiceNodeTester::TestRequestSerializationError() {
  WebServiceAdapter<TestServiceA>::Request::Signal::slot_type slot =
    m_sessionSink->GetSlot<WebServiceAdapter<TestServiceA>::Request::Signal>();
  m_webServiceNode->ConnectRequestSignal<TestServiceA>(
    MatchesPath(HttpMethod::POST, "/test_a"), slot);
  m_webServiceNode->ConnectInvalidRequestSignal(
    m_sessionSink->GetSlot<HttpSessionHandler::HttpRequestSignal>());

  // Send an initial request message with an invalid body.
  string body = "asdasdasdas";
  string requestMessage =
"POST /test_a HTTP/1.1\r\n"
"Content-Length: " + lexical_cast<string>(body.size()) + "\r\n"
"Host: www.test.com\r\n\r\n" + body;
  m_channel->GetWriter().Write(Buffer(requestMessage.c_str(),
    requestMessage.size()));
  SignalSink::SignalEntry entry = m_sessionSink->GetNextSignal(seconds(1));
  CPPUNIT_ASSERT(*entry.m_type ==
    typeid(HttpSessionHandler::HttpRequestSignal));
  HttpSession* session = any_cast<HttpSession*>(entry.m_parameters[0]);
  CPPUNIT_ASSERT(session != NULL);
  HttpServerRequest* request = any_cast<HttpServerRequest*>(
    entry.m_parameters[1]);
  HttpServerResponse* response = any_cast<HttpServerResponse*>(
    entry.m_parameters[2]);
  response->SendResponse();
}
